Skip to content

feat(canvas): Phase 2 — SSE run panel with live streaming#12

Merged
prosdev merged 9 commits intomainfrom
plan/canvas-phase-2
Mar 17, 2026
Merged

feat(canvas): Phase 2 — SSE run panel with live streaming#12
prosdev merged 9 commits intomainfrom
plan/canvas-phase-2

Conversation

@prosdev
Copy link
Contributor

@prosdev prosdev commented Mar 17, 2026

Summary

  • SSE streaming service layer with auto-reconnection (3 attempts, exponential backoff)
  • Zustand run slice managing run lifecycle (idle → running → paused → completed/error)
  • Run panel (bottom Sheet) showing live event stream with node highlighting
  • Run button with graph validation (connected, has start/end)
  • Human-input pause/resume flow
  • Run panel polish: UUIDs resolved to node labels, timestamps, provider/model subtitles for LLM nodes, bordered output blocks

Screenshot

Run panel showing Start → LLM → End graph execution

Start → LLM → End graph with gemini-2.0-flash producing a haiku. Panel shows resolved labels, duration, provider/model, and output.

Key files

Area Files
Service layer packages/canvas/src/api/runs.ts
Store packages/canvas/src/store/runSlice.ts
Run panel packages/canvas/src/components/panels/RunPanel.tsx, RunEventItem.tsx, ResumeForm.tsx
Run button packages/canvas/src/components/canvas/RunButton.tsx, RunInputDialog.tsx
Utils packages/canvas/src/utils/format.ts

Test plan

  • Start → LLM → End graph runs successfully, panel shows labels not UUIDs
  • Timestamps display on run_started and node_started events
  • Provider/model subtitle shows for LLM nodes (gemini · gemini-2.0-flash)
  • Edge events show resolved labels (LLM → End)
  • No redundant "Completed" row — title shows "Run completed 1.0s"
  • Spinner shows during node execution, replaced by checkmark on completion
  • Reconnection on SSE drop (kill Docker mid-run, verify 3 retry attempts)
  • Human-input pause/resume flow
  • Error display with node label prefix

🤖 Generated with Claude Code

prosdev and others added 9 commits March 16, 2026 14:55
Five-part plan covering SSE service layer, RunSlice state machine,
run button with validation, run panel with node highlighting, and
reconnection with human-in-the-loop resume. Two review passes
addressed race conditions (terminal event + onerror, concurrent
reconnection), auth via Vite proxy, and Sheet bottom variant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 2.1: SSE service layer
- Export apiUrl() from client.ts for shared URL construction
- Rewrite runs.ts: startRun, resumeRun, cancelRun, getRunStatus,
  connectStream with typed SSE events and Last-Event-ID support
- All request bodies wrap input in { input: ... } per server schema

Phase 2.2: RunSlice state machine
- Full event dispatch for all 7 GraphEvent types
- terminalReceived flag prevents onerror race after graph_completed
- Module-closure cleanup ref (not in Zustand state)

Phase 2.3: Run button + validation
- Run/Stop button in CanvasHeader with status-driven states
- validateGraph checks Start/End presence, orphans, LLM prompts
- Auto-save before run with error checking
- RunInputDialog for JSON input

Phase 2.4: Run panel + node highlighting
- Sheet extended with side="bottom" (openTransform/closedTransform)
- RunPanel with event timeline, auto-scroll, status banners
- Active node pulse via useNodeId() + useRunStore in BaseNodeShell

Phase 2.5: Reconnection + resume
- Exponential backoff (1s→2s→4s, max 3 attempts)
- Status polling fallback (completed/running/paused/error)
- reconnecting concurrency guard prevents parallel chains
- ResumeForm for human-in-the-loop paused runs
- Fix stale URL in gw-frontend skill

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Validate JSON input is a plain object (not string/array/null)
- Reset terminalReceived in resumeRun to prevent stale guard
- Add Number.isFinite guard on lastEventId query param
- Extract duplicate formatDuration to utils/format.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The canvas saves schema_json without the top-level `id` field (it's
stored as a separate DB column). But validate_schema requires `id`.
Inject graph.id into the schema dict before calling build_graph and
validate_schema in both the run and validate endpoints.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The default state only has a "messages" field. The previous default
"result" caused a 422 from the server because the output_key must
reference an existing state field.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LLM node: when input_map is empty (default), use messages from state
instead of building an empty HumanMessage. Gemini rejects empty content
with ValueError. Falls back to "Begin." if state messages are also empty.

Also include tracebacks in structured JSON logs — the JSONFormatter
previously discarded exc_info from logger.exception calls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
gemini-1.5-flash was retired from the API. Update the default
provider to gemini and model to gemini-2.0-flash.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace raw node IDs with human-readable labels, add timestamps,
show provider/model for LLM nodes, and remove redundant graph_completed row.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace recursive reconnection with iterative loop + .catch() safety net
- Fix _handleStreamError type signature (void | Promise<void>)
- Add stale-state guards (terminalReceived/reconnecting) after each await
- Truncate node output display at 2000 chars to prevent DOM bloat
- Reset RunInputDialog state on reopen
- Clear ResumeForm input after submit
- Update gw-frontend skill with Phase 2 patterns and accurate RunSlice shape

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@prosdev prosdev merged commit 428c4b7 into main Mar 17, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant